<?php

	/**
     * Installer
     *
	 * @package      Core
     * 
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
	 */
	class Core_Installer
    {

	////////////////////////////////////////////////////////////////////////////
	//                                Variables                               //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Singleton instance
         *
         * Marked only as protected to allow extension of the class.
         * To extend, simply override {@link getInstance()}.
         *
         * @var     Core_Installer
         */
        protected static $_instance = null;

        /**
         * Pathes
         *
         * @var     array
         */
        protected $_pathes = array();

        /**
         * Data
         *
         * @var     array
         */
        protected $_data = array();

	////////////////////////////////////////////////////////////////////////////
	//                            Getters / Setters                           //
	////////////////////////////////////////////////////////////////////////////


	////////////////////////////////////////////////////////////////////////////
	//                             Static Methods                             //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Singleton instance
         *
         * @return      Core_Installer
         */
        public static function getInstance()
        {
            if (self::$_instance === null) {
                self::$_instance = new self();
            }

            return self::$_instance;
        }

	////////////////////////////////////////////////////////////////////////////
	//                                 Methods                                //
	////////////////////////////////////////////////////////////////////////////

        /**
		 * Constructor
         *
         * Project structure :
         *
         * project/
         *      app/
         *          modules/
         *          plugins/
         *          var/
         *              backups/
         *              config/
         *              log/
         *                  updates/
         *      lib/
         *      serum/
         *      skin/
         *          frontend/
         *              css/
         *              images/
         *              js/
         *              templates/
         *          backend/
         *              modules/
         *      temp/
         *          compiled/
         *              urls/
         *              locales/
         *              templates/
         *      updates/
         *          migrations/
         *      index.php
         *      json.php
         *      xml.php
		 */
		public function __construct()
        {
            $this->_pathes['PROJECT_WWW_PATH']                          = PROJECT_WWW_PATH;

            $this->_pathes['PROJECT_WWW_PATH_APP']                      = PROJECT_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_MODULES']              = PROJECT_MODULE_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_PLUGINS']              = PROJECT_PLUGIN_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_VAR']                  = PROJECT_VAR_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_VAR_BACKUPS']          = PROJECT_BACKUP_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_VAR_CONFIG']           = PROJECT_CONFIG_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_VAR_LOG']              = PROJECT_LOG_PATH;
            $this->_pathes['PROJECT_WWW_PATH_APP_VAR_LOG_UPDATES']      = Core_IO_Path::combine(PROJECT_LOG_PATH, 'updates');

            $this->_pathes['PROJECT_WWW_PATH_INSTALL']                  = PROJECT_INSTALL_PATH;
            $this->_pathes['PROJECT_WWW_PATH_MEDIA']                    = PROJECT_MEDIA_PATH;
            $this->_pathes['PROJECT_WWW_PATH_LIB']                      = PROJECT_LIB_PATH;
            $this->_pathes['PROJECT_WWW_PATH_SERUM']                    = Core_IO_Path::combine(PROJECT_WWW_PATH, 'serum');
            $this->_pathes['PROJECT_WWW_PATH_SKIN']                     = Core_IO_Path::combine(PROJECT_WWW_PATH, 'skin');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND']            = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN'], 'frontend');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_CSS']        = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND'], 'css');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_IMAGES']     = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND'], 'images');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_JS']         = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND'], 'js');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_TEMPLATES']  = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND'], 'templates');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND']             = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN'], 'backend');
            $this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND_MODULES']     = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND'], 'modules');

            $this->_pathes['PROJECT_WWW_PATH_TEMP']                             = PROJECT_TEMP_PATH;
            $this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED']                    = PROJECT_TEMP_COMPILED_PATH;
            $this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_URLS']               = PROJECT_TEMP_URLS;
            $this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_LOCALES']            = PROJECT_TEMP_LOCALES;
            $this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES']          = PROJECT_TEMP_TEMPLATES;
            $this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES_CACHE']    = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES'], 'cache');
            $this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES_COMPILED'] = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES'], 'compiled');

            $this->_pathes['PROJECT_WWW_PATH_UPDATES']                      = PROJECT_UPDATE_ROOT_PATH;
            $this->_pathes['PROJECT_WWW_PATH_UPDATES_MIGRATIONS']           = PROJECT_UPDATE_PATH;

            $this->_pathes['HTACCESS']                                      = Core_IO_Path::combine(WWW_PATH, '.htaccess');
            $this->_pathes['CONFIG_FILE']                                   = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_APP_VAR_CONFIG'], 'global.ini.php');
            $this->_pathes['WS_UPDATER']                                    = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_APP_VAR_CONFIG'], 'updater.wsdl');
            $this->_pathes['FIXTURES']                                      = Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH_INSTALL'], 'fixtures.yml');

            // Check environnement
            if (Core_IO_File::exists($this->_pathes['CONFIG_FILE'])) {
                throw new Core_Exception('Serum is already installed');
            }
        }

		/**
		 * Destructor
		 */
		public function __destruct()
		{
			unset($this);
		}

        /**
         * Process
         *
         * @param       array       $data
         * @return      void
         */
        public function check(array $data)
        {
            $this->_data = $data;

            $basePath                   = explode('/', $_SERVER['REQUEST_URI']);
            $basePath                   = array_slice($basePath, 0, -3);
            $basePath                   = implode('/', $basePath);
            $this->_data['basePath']    = $basePath .'/';

            try {
                $connection = Doctrine_Manager::connection($data['fieldAdapter'] .'://'. $data['fieldUsername'] .':'. $data['fieldPassword'] .'@'. $data['fieldHost'] .'/'. $data['fieldDbname']);
                $connection->connect();
                $connection->close();

            } catch (Exception $e) {
                throw new Core_Installer_Exception(array('fieldAdapter' => $e->getMessage()));
            }
            
            if (!Doctrine_Validator_Email::validate($data['fieldEmail'])) {
                throw new Core_Installer_Exception(array('fieldEmail' => 'The given value is a not valid email address'));
            }
        }

        /**
         * Creates directories and files
         *
         * @return      void
         */
        public function process()
        {
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH']);

            /*
             * Checkout
             */
            $svn = null;

            if (!empty($this->_data['svn_source'])) {
                $svn = Core_Svn_Adapter::factory($this->_data['svn_client']);

                $svn->setLogin($this->_data['svn_login']);
                $svn->setPassword($this->_data['svn_password']);
                $svn->setSource($this->_data['svn_source']);
                $svn->setBinary($this->_data['svn_binary']);
                $svn->setDestination($this->_pathes['PROJECT_WWW_PATH']);
                $svn->checkout();
            }

            /*
             * Directories
             */
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_MODULES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_PLUGINS']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_VAR']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_VAR_BACKUPS']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_VAR_CONFIG']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_VAR_LOG']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_APP_VAR_LOG_UPDATES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_INSTALL']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_LIB']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_MEDIA']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SERUM']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_CSS']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_IMAGES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_JS']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_TEMPLATES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND_MODULES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_URLS']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_LOCALES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES_CACHE']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_TEMP_COMPILED_TEMPLATES_COMPILED']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_UPDATES']);
            $this->_mkdir($this->_pathes['PROJECT_WWW_PATH_UPDATES_MIGRATIONS']);

            /*
             * index.php, json.php, xml.php
             */
            foreach (array(
                            'admin' => Core_IO_Path::combine($this->_pathes['PROJECT_WWW_PATH'], 'serum'),
                            'front' => $this->_pathes['PROJECT_WWW_PATH']
                          ) as $controllerEnd => $controllerPath) {

                foreach (array(
                                'index' => 'http',
                                'json'  => 'json',
                                'xml'   => 'xml'
                              ) as $controllerFile => $controllerType) {

                    $path    = Core_IO_Path::combine($controllerPath, $controllerFile .'.php');

                    $content = '<?php' . PHP_EOL;
                    $content .= PHP_EOL;
                    $content .= '    if (version_compare(phpversion(), \'5.2.0\', \'<\') === true) {'. PHP_EOL;
                    $content .= '        exit(1);'. PHP_EOL;
                    $content .= '    }'. PHP_EOL;
                    $content .= PHP_EOL;
                    $content .= '    require_once \''. SERUM_PATH .'Main.php\';'. PHP_EOL;
                    $content .= PHP_EOL;
                    $content .= '    umask(0);'. PHP_EOL;
                    $content .= '    Main::run(\''. $controllerEnd .'\', \''. $controllerType . '\', (isset($_GET[\'generate\']) && $_GET[\'generate\'] == \'true\'), (isset($_GET[\'data\']) && $_GET[\'data\'] == \'true\'));'. PHP_EOL;

                    Core_IO_File::writeAll($path, $content);
                }
            }

            /*
             * /project/app/var/config.ini.php
             * /.htaccess
             */
            Core_IO_File::writeAll($this->_pathes['CONFIG_FILE'], Core_Installer_Adapter::factory(Core_Installer_Adapter::ADAPTER_CONFIG)->process($this->_data));
            Core_IO_File::writeAll($this->_pathes['WS_UPDATER'], Core_Installer_Adapter::factory(Core_Installer_Adapter::ADAPTER_WSUPDATER)->process($this->_data));
            Core_IO_File::writeAll($this->_pathes['HTACCESS'], Core_Installer_Adapter::factory(Core_Installer_Adapter::ADAPTER_HTACCESS)->process($this->_data));
            Core_IO_File::writeAll($this->_pathes['FIXTURES'], Core_Installer_Adapter::factory(Core_Installer_Adapter::ADAPTER_FIXTURES)->process($this->_data));

            /*
             * Only if .svn does not exist
             */
            if (!empty($this->_data['svn_source']) && $svn !== null) {
                $svn->add($this->_pathes['PROJECT_WWW_PATH_APP']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_APP_MODULES']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_APP_PLUGINS']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_APP_VAR']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_INSTALL']);
                $svn->add($this->_pathes['FIXTURES']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_LIB']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_MEDIA']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SERUM']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_CSS']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_IMAGES']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_JS']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_FRONTEND_TEMPLATES']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_SKIN_BACKEND_MODULES']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_TEMP']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_UPDATES']);
                $svn->add($this->_pathes['PROJECT_WWW_PATH_UPDATES_MIGRATIONS']);

                $ignores = Core_Installer_Adapter::factory(Core_Installer_Adapter::ADAPTER_IGNORE)->process($this->_data);

                foreach ($ignores as $ignore_path => $ignore_pattern) {
                    if ($ignore_path == '*') {
                        $svn->ignore($ignore_pattern, '.', true);
                    } else {
                        $svn->ignore($ignore_pattern, $ignore_path);
                    }
                }
            }

            return $this->_data['basePath'];
        }

        /**
         * Create directory shortcut
         *
         * @param       string      $path
         */
        private function _mkdir($path)
        {
            if (!Core_IO_Directory::exists($path)) {
                Core_IO_Directory::createDirectory($path);
            }
        }
    }